
#include "obj3d.h"
#include <QFile>
#include <QTextStream>

#include <ios>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>

Obj3D::Obj3D()
{
    rs[0] = 0.0; rs[1] = 0.0; rs[2] = 0.0;
    ts[0] = 0.0; ts[1] = 2.2; ts[2] = 0.0;
    ss[0] = 0.7; ss[1] = 0.7; ss[2] = 0.7;
    default_rs[0] = 0.0; default_rs[1] = 0.0; default_rs[2] = 0.0;
    default_ts[0] = 0.0; default_ts[1] = 2.2; default_ts[2] = 0.0;
    default_ss[0] = 0.7; default_ss[1] = 0.7; default_ss[2] = 0.7;

    speedVector[0] = 0.0;
    speedVector[1] = 0.0;
    speedVector[2] = 0.0;

}




void Obj3D::setObjName(char* name){
    obj_name = name;
}

void Obj3D::setObjID(int id){
    objID = id;
}

void Obj3D::clearMesh(){

    obj.vertices.clear( );
    obj.normals.clear( );
    obj.elements.clear( );
    obj.face_vertices.clear( );


    g_BVMesh.vertices.clear( );
    g_BVMesh.normals.clear( );
    g_BVMesh.elements.clear( );
    g_BVMesh.face_vertices.clear( );

    if(objID == 0){
        for(int i = 0; i < 5; i++){

            bound[i].vertices.clear( );
            bound[i].normals.clear( );
            bound[i].elements.clear( );
            bound[i].face_vertices.clear( );
        }
    }

}


void Obj3D::static_matrix(float rot[3],float trans[3],float scale[3]){
    mat4 staticM = Angel::Translate(trans[0],trans[1],trans[2])*Angel::RotateX(rot[0])*Angel::RotateY(rot[1])*Angel::RotateZ(rot[2])*Angel::Scale(scale[0],scale[1],scale[2]);

}

void Obj3D::setTranslate(vec3 t){
    ts[0] = t[0];
    ts[1] = t[1];
    ts[2] = t[2];
    setPosition(t);
}
void Obj3D::setRotate(vec3 r){

    rs[0] = r[0];
    rs[1] = r[1];
    rs[2] = r[2];
}

void Obj3D::setScale(vec3 s){

    ss[0] = s[0];
    ss[1] = s[1];
    ss[2] = s[2];
}
void Obj3D::setTranslate(float t[]){
    ts[0] = t[0];
    ts[1] = t[1];
    ts[2] = t[2];
}
void Obj3D::setRotate(float r[]){

    rs[0] = r[0];
    rs[1] = r[1];
    rs[2] = r[2];
}


void Obj3D::setRotateY(float a){
    rs[1] = a;
}

void Obj3D::setScale(float s[]){

    ss[0] = s[0];
    ss[1] = s[1];
    ss[2] = s[2];
}

void Obj3D::setDefaultTranslate(float t[]){
    default_ts[0] = t[0];
    default_ts[1] = t[1];
    default_ts[2] = t[2];
}
void Obj3D::setDefaultRotate(float r[]){

    default_rs[0] = r[0];
    default_rs[1] = r[1];
    default_rs[2] = r[2];
}

void Obj3D::setDefaultScale(float s[]){

    default_ss[0] = s[0];
    default_ss[1] = s[1];
    default_ss[2] = s[2];
}


void Obj3D::restore(){
    for(int i = 0 ; i < 3; i++){
        rs[i] = default_rs[i];
        ts[i] = default_ts[i];
        ss[i] = default_ss[i];
    }
    m_boundingBox.setGlobalCenter(m_boundingBox.getLocalCenter());
}

void Obj3D::updateM(){
    objM = Angel::Translate(ts[0],ts[1],ts[2])*Scale(ss[0], ss[1], ss[2])*RotateX(rs[0])*RotateY(rs[1])*RotateZ(rs[2]);

    if(objID == 0){
        for(int i = 0; i < 5; i++){
            boundM[i] = Angel::Translate(ts[0],ts[1],ts[2])*RotateX(rs[0])*RotateY(rs[1])*RotateZ(rs[2]);

        }
    }
}


void Obj3D::run(){
    while(true){

        ts[0] += speedVector[0];
        ts[1] += speedVector[1];
        ts[2] += speedVector[2];

        this->msleep(16);
    }
}

void Obj3D::readBound0(){
    char* name = "head.obj";
    readObject(
        name,
        bound[0].vertices,
        bound[0].face_vertices,
        bound[0].normals,
        bound[0].elements,
        bound[0].flat_vertices,
        bound[0].flat_normals);

}
void Obj3D::readBound1(){
    char* name1 = "wing.obj";
    readObject(
                name1,
        bound[1].vertices,
        bound[1].face_vertices,
        bound[1].normals,
        bound[1].elements,
        bound[1].flat_vertices,
        bound[1].flat_normals);


}
void Obj3D::readBound2(){
    char* name2 = "body.obj";
    readObject(
                name2,
        bound[2].vertices,
        bound[2].face_vertices,
        bound[2].normals,
        bound[2].elements,
        bound[2].flat_vertices,
        bound[2].flat_normals);


}
void Obj3D::readBound3(){
    char* name3 = "tail.obj";
    readObject(
                name3,
        bound[3].vertices,
        bound[3].face_vertices,
        bound[3].normals,
        bound[3].elements,
        bound[3].flat_vertices,
        bound[3].flat_normals);


}
void Obj3D::readBound4(){
    char* name4 = "tail_up.obj";
    readObject(
                name4,
        bound[4].vertices,
        bound[4].face_vertices,
        bound[4].normals,
        bound[4].elements,
        bound[4].flat_vertices,
        bound[4].flat_normals);

}

bool Obj3D::checkCollision(Obj3D* objB){
    vec3 center = objB->getBoundingBox().getGlobalCenter();
    GLfloat len =
            (m_boundingBox.getGlobalCenter().x-center.x)*(m_boundingBox.getGlobalCenter().x-center.x)
            +(m_boundingBox.getGlobalCenter().y-center.y)*(m_boundingBox.getGlobalCenter().y-center.y)
            +(m_boundingBox.getGlobalCenter().z-center.z) * (m_boundingBox.getGlobalCenter().z-center.z);
    len = sqrt(len);

    if(len > maxR + objB->maxR){
        return false;
    }else{

        for(int k = 1; k < 2 ; k++){
            for(int i=0; i<bound[k].vertices.size(); i++){
                GLfloat temp =
                        (bound[k].vertices[i].x-center.x)*(bound[k].vertices[i].x-center.x)
                        +(bound[k].vertices[i].y-center.y)*(bound[k].vertices[i].y-center.y)
                        +(bound[k].vertices[i].z-center.z) * (bound[k].vertices[i].z-center.z);
                if(sqrt(temp) < objB->maxR){
                    return true;
                }
            }
        }

    }
    return false;
}

void Obj3D::loadFromFile()
{
    m_pos = vec3(default_ts[0],default_ts[1],default_ts[2]);



    readObject(
        obj_name,
        obj.vertices,
        obj.face_vertices,
        obj.normals,
        obj.elements,
        obj.flat_vertices,
        obj.flat_normals);



    constructBoundingVolume();
}
void Obj3D::loadFromFile(char* name)
{
    m_pos = vec3(default_ts[0],default_ts[1],default_ts[2]);



    readObject(
        name,
        obj.vertices,
        obj.face_vertices,
        obj.normals,
        obj.elements,
        obj.flat_vertices,
        obj.flat_normals);



    constructBoundingVolume();
}


void Obj3D::constructBoundingVolume()
{
    //////////////////////////////////////////////////// TODO Start
    vec3 min = vec3(OBJECT_MAX_RANGE);
    vec3 max = vec3(OBJECT_MIN_RANGE);
    vec3 localCenter;
    // For bounding box
    for(int i=0; i<obj.vertices.size(); i++)
    {
        if(obj.vertices[i].x > max.x) max.x = obj.vertices[i].x;
        if(obj.vertices[i].y > max.y) max.y = obj.vertices[i].y;
        if(obj.vertices[i].z > max.z) max.z = obj.vertices[i].z;
        if(obj.vertices[i].x < min.x) min.x = obj.vertices[i].x;
        if(obj.vertices[i].y < min.y) min.y = obj.vertices[i].y;
        if(obj.vertices[i].z < min.z) min.z = obj.vertices[i].z;
    }
    localCenter = (max + min) * 0.5;
    m_boundingBox.setLocalCenter(localCenter);
    m_boundingBox.setGlobalCenter(localCenter);
    m_boundingBox.setHalfWidth((max - min) * 0.5);


    maxR = 0;
    for(int i=0; i<obj.vertices.size(); i++)
    {
        GLfloat temp =
                (obj.vertices[i].x-localCenter.x)*(obj.vertices[i].x-localCenter.x)
                +(obj.vertices[i].y-localCenter.y)*(obj.vertices[i].y-localCenter.y)
                +(obj.vertices[i].z-localCenter.z) * (obj.vertices[i].z-localCenter.z);
        if(maxR < temp){
            maxR = temp;
        }
    }
    maxR = sqrt(maxR);


}

void Obj3D::setPosition(vec3 pos)
{
    m_pos = pos;
    m_boundingBox.setPosition(pos);
}

vec3 Obj3D::getPosition()
{
    return m_pos;
}

void Obj3D::translate(vec3 translation)
{
    m_pos = m_pos + translation;
    m_boundingBox.setPosition(m_pos);
}

BoundingBox Obj3D::getBoundingBox()
{
    return m_boundingBox;
}
bool Obj3D::testBoundingBoxIntersection(Obj3D* objectB)
{
    return m_boundingBox.testIntersection(objectB->getBoundingBox().getGlobalCenter(), objectB->getBoundingBox().getHalfWidth());
}


void Obj3D::readObject(){
    readObject(
        obj_name,
        obj.vertices,
        obj.face_vertices,
        obj.normals,
        obj.elements,
        obj.flat_vertices,
        obj.flat_normals);
}

void Obj3D::readObject(
        const char* OBJFilePath,
        std::vector<vec4> &out_vertices,
        std::vector<vec4> &out_face_vertices,
        std::vector<vec3> &out_normals,
        std::vector<GLushort> &out_elements,
        std::vector<vec4> &flat_vertices,
        std::vector<vec3> &flat_normals){

    std::vector<vec4> temp_normal;
    // Reading the OBJ file
    std::ifstream in(OBJFilePath, std::ios::in);
    if( !in ) {
        std::cerr << "Cannot open" << OBJFilePath << std::endl;
        exit(1);
    }
    // Parsing the OBJ file
    std::string line;
    while( std::getline(in, line) ) {
        if ( line.substr(0,2) == "v " ) {
            std::istringstream pos(line.substr(2));
            vec4 vertex;
            pos >> vertex.x; pos >> vertex.y; pos >> vertex.z;
            vertex.w = 1.0f;
            out_vertices.push_back(vertex);
        } else if ( line.substr(0, 2) == "f " ) {
            std::istringstream idx(line.substr(2));
            GLushort a, b, c;
            idx >> a;
            idx >> b;
            idx >>c;
            a--; b--; c--;
            out_elements.push_back(a); out_elements.push_back(b); out_elements.push_back(c);

		} else if ( line.substr(0,1) == "#") {
            // Ignore comments
        } else {}
    }

    // Flat shading - compute each face normal

    // Calculating the normal of each faces
    out_normals.resize( out_vertices.size(), vec3(0.0, 0.0, 0.0) );
    temp_normal.resize(out_vertices.size(),vec4(0.0,0.0,0.0,0.0f));

    //out_face_vertices.resize( out_vertices.size(), vec3(0.0, 0.0, 0.0) );

    std::vector<int> NumOfFace;
    NumOfFace.resize( out_vertices.size(), 0 );

    for( int i=0; i < out_elements.size(); i+=3 ) {

        // Ex-TODO: compute the normal
        // triangle indices are stored in ��out_elements��
        GLushort index_a = out_elements[i];
        GLushort index_b = out_elements[i+1];
        GLushort index_c = out_elements[i+2];

        // Compute the normal(CCW)
        vec3 normal =
        normalize(cross(out_vertices[index_b] - out_vertices[index_a],
                out_vertices[index_c] - out_vertices[index_a]));
        //printf("normal %d,%d,%d\n",normal.x,normal.y,normal.z);

        //Face normal to each vertex
        //out_normals[index_a] = out_normals[index_b] = out_normals[index_c] = normal;
        temp_normal[index_a]+= vec4(normal,1);
        temp_normal[index_b]+= vec4(normal,1);
        temp_normal[index_c]+= vec4(normal,1);
        out_face_vertices.push_back((1/3.0f*(out_vertices[index_a]+out_vertices[index_b]+out_vertices[index_c])));
        out_face_vertices.push_back((1/3.0f*(out_vertices[index_a]+out_vertices[index_b]+out_vertices[index_c])));
        out_face_vertices.push_back((1/3.0f*(out_vertices[index_a]+out_vertices[index_b]+out_vertices[index_c])));

        // prepare flat normal.
        flat_normals.push_back(normal);
        flat_normals.push_back(normal);
        flat_normals.push_back(normal);
    }

    // Averaging
    for(int i=0;i<out_elements.size();i++)
    {

        temp_normal[out_elements[i]]/=temp_normal[out_elements[i]].w;

        vec4 normal_4 =temp_normal[out_elements[i]];
        vec3 normal = vec3(normal_4.x,normal_4.y,normal_4.z);
        out_normals[out_elements[i]] = normal;

        flat_vertices.push_back(out_vertices[out_elements[i]]);

    }
/*
    QFileStream *objFile = new QFile(QString("airplane1.obj"));
    objFile->open(QIODevice::ReadOnly);


    std::vector<vec4> temp_normal;
    QTextStream stream(objFile);

    QString line;
    while(!stream.atEnd()) {
         line = stream.readLine();
         if(line.startsWith("v ")){
             vec4 vertex;
             line.remove(0,2);
             QStringList list = line.split(" ");
             vertex.x = list[0].toFloat();
             vertex.y = list[0].toFloat();
             vertex.y = list[0].toFloat();
             vertex.w = 1.0f;
             out_vertices.push_back(vertex);
         }
         else if(line.startsWith("f ")){
             GLushort a,b,c;
             line.remove(0,2);
             QStringList list = line.split(" ");
             a = list[0].toShort();
             b = list[0].toShort();
             c = list[0].toShort();
             out_elements.push_back(a); out_elements.push_back(b); out_elements.push_back(c);

         }else if(line.startsWith("#")){
             //it is just comment
         }

    }

    // Flat shading - compute each face normal

    // Calculating the normal of each faces
    out_normals.resize( out_vertices.size(), vec3(0.0, 0.0, 0.0) );
    temp_normal.resize(out_vertices.size(),vec4(0.0,0.0,0.0,0.0f));

    //out_face_vertices.resize( out_vertices.size(), vec3(0.0, 0.0, 0.0) );

    std::vector<int> NumOfFace;
    NumOfFace.resize( out_vertices.size(), 0 );

    for( int i=0; i < out_elements.size(); i+=3 ) {

        // Ex-TODO: compute the normal
        // triangle indices are stored in ��out_elements��
        GLushort index_a = out_elements[i];
        GLushort index_b = out_elements[i+1];
        GLushort index_c = out_elements[i+2];

        // Compute the normal(CCW)
        vec3 normal =
        normalize(cross(out_vertices[index_b] - out_vertices[index_a],
                out_vertices[index_c] - out_vertices[index_a]));
        //printf("normal %d,%d,%d\n",normal.x,normal.y,normal.z);

        //Face normal to each vertex
        //out_normals[index_a] = out_normals[index_b] = out_normals[index_c] = normal;
        temp_normal[index_a]+= vec4(normal,1);
        temp_normal[index_b]+= vec4(normal,1);
        temp_normal[index_c]+= vec4(normal,1);
        out_face_vertices.push_back((1/3.0f*(out_vertices[index_a]+out_vertices[index_b]+out_vertices[index_c])));
        out_face_vertices.push_back((1/3.0f*(out_vertices[index_a]+out_vertices[index_b]+out_vertices[index_c])));
        out_face_vertices.push_back((1/3.0f*(out_vertices[index_a]+out_vertices[index_b]+out_vertices[index_c])));

        // prepare flat normal.
        flat_normals.push_back(normal);
        flat_normals.push_back(normal);
        flat_normals.push_back(normal);
    }

    // Averaging
    for(int i=0;i<out_elements.size();i++)
    {

        temp_normal[out_elements[i]]/=temp_normal[out_elements[i]].w;

        vec4 normal_4 =temp_normal[out_elements[i]];
        vec3 normal = vec3(normal_4.x,normal_4.y,normal_4.z);
        out_normals[out_elements[i]] = normal;

        flat_vertices.push_back(out_vertices[out_elements[i]]);

    }
    */

}
